为保证API的安全调用,在调用API时阿里云会对每个API请求通过签名(Signature)进行身份验证。无论使用HTTP还是HTTPS协议提交请求,都需要在请求中包含签名信息。

签名算法

本文介绍函数计算API签名步骤,SDK中已经实现了签名算法并对API进行签名,无需您手动计算。具体信息,请参见SDK列表

对于每一个请求,函数计算服务会根据请求头部的Authorization字段来校验是否合法(设置了HTTP触发器的允许匿名访问的函数除外)。客户端须使用与函数计算服务端一致的签名算法才能通过验证,对于未包含签名字段或者签名错误的请求,函数计算将会返回HTTP 403错误。

说明 函数计算返回HTTP 403错误时,不会产生费用。关于计费的详细信息,请参见计费概述
signature = base64(hmac-sha256(HTTP_METHOD + "\n" 
                + CONTENT-MD5 + "\n"     
                + CONTENT-TYPE + "\n" 
                + DATE + "\n" 
                + CanonicalizedFCHeaders
                + CanonicalizedResource))

// Authorization字段介绍                
Authorization = "FC " + accessKeyID + ":" + signature            

参数说明如下:

  • HTTP_METHOD:大写的HTTP方法,例如PUT、GET、POST、DELETE。
  • CONTENT-MD5:请求内容数据的MD5值。如果请求的Header中没有传Content-MD5,则此处填入空串。
  • CONTENT-TYPE:请求内容的类型,函数计算的类型是application/json
  • DATE:此次操作的时间,不能为空,支持时区为GMT的RFC1123格式,例如Mon, 02 Jan 2006 15:04:05 GMT
    注意 客户端需要保证生成的时间与函数计算服务端的时间相差不超过15分钟,否则函数服务将拒绝此次请求。
  • CanonicalizedFCHeaders:所有以x-fc-为前缀的HTTP头组成的字符串。生成方式,请参见CanonicalizedFCHeaders
  • CanonicalizedResource:请求的URL的Path,一般是先对收到的Path解码,再去掉请求的Path里的Params内容。
    • Path的结构为:$api-version/api-path
      • api-version:API版本,目前版本为2016-08-15
      • api-path:访问各个接口的路径,例如创建Service为/services,其他Path,请参见API概览
    • 需要认证的HTTP触发器与其他请求的CanonicalizedResource不同,下面对这两种情况分别进行介绍。
      • 需要认证的HTTP触发器请求:如果有Params,则以回车符\n分隔各个参数,Params中的各个参数key-value对按照字母序进行排序。如果没有Params则以\n补齐。 例如:
        // 需要认证的HTTP触发器的URL的真实Path
        /2016-08-15/proxy/service-name/func-name/path-with-%20-space/action?x=1&a=2&x=3&with%20space=foo%20bar
        
        // URL decode后的结果
        /2016-08-15/proxy/service-name/func-name/path-with- -space/action?x=1&a=2&x=3&with space=foo bar
        
        // 需要认证的HTTP触发器的CanonicalizedResource
        /2016-08-15/proxy/service-name/func-name/path-with- -space/action\na=2\nwith space=foo bar\nx=1\nx=3
        
        // 普通请求的URL的真实Path
        /2016-08-15/service-name/func-name/path-with-%20-space/action?x=1&a=2&x=3&with%20space=foo%20bar
        
        // URL解码后的结果
        /2016-08-15/service-name/func-name/path-with- -space/action?x=1&a=2&x=3&with space=foo bar
        
        // 普通请求的CanonicalizedResource
        /2016-08-15/service-name/func-name/path-with- -space/action                    
        说明 如果Params里的key对应多个value,即对key-value整体进行排序。
      • 普通请求:首先对收到的Path进行URL解码,普通请求的CanonicalizedResource会只取到?前面的内容,即舍弃传入的各个Params
        说明 普通请求是除了需要访问带认证的HTTP触发器的请求外的所有请求。
  • hmac-sha256:需要以您的AccessKey Secret为Key。

    您可以通过以下伪代码确认签名的实现方式:

    // 构造字符串的过程
    function composeStringToSign(method, path, headers, queries) {
      var contentMD5 = headers['content-md5'] || '';
      var contentType = headers['content-type'] || '';
      var date = headers['date'];
      var signHeaders = buildCanonicalHeaders(headers, 'x-fc-');
    
      var u = url.parse(path);
      var pathUnescaped = decodeURIComponent(u.pathname);
      var str = `${method}\n${contentMD5}\n${contentType}\n${date}\n${signHeaders}${pathUnescaped}`;
    
      if (queries) {
        var params = [];
        Object.keys(queries).forEach(function (key) {
          var values = queries[key];
          var type = typeof values;
          if (type === 'string') {
            params.push(`${key}=${values}`);
            return;
          }
          if (type === 'object' && values instanceof Array) {
            queries[key].forEach(function (value) {
              params.push(`${key}=${value}`);
            });
          }
        });
        params.sort();
        str += '\n' + params.join('\n');
      }
      return str;
    }
    
    // 使用HMAC-SHA256和Base64计算签名的过程,其中Source参数为构造出的字符串。
    function signString(source, secret) {
      const buff = crypto.createHmac('sha256', secret)
        .update(source, 'utf8')
        .digest();
      return new Buffer(buff, 'binary').toString('base64');
    }                   

CanonicalizedFCHeaders

完成以下操作,构造阿里云规范头,操作步骤如下:

  1. 将所有以x-fc-为前缀的请求头的开头字段转换成小写字母。
  2. 对于每一个字段,生成一个子串${key}:${value}\n ,例如X-Fc-Invocation-Type:Sync转换成x-fc-invocation-type:Sync\n,这些字段将会按字段名从小到大排序。
    • ${key}:HTTP头的名称。
    • ${value}:HTTP头的值。
  3. 将上述生成的子串连接成一个整串。
    您可以通过以下伪代码确认签名的实现方式:
    // javascript
    // prefix = 'x-fc-'
    function buildCanonicalHeaders(headers, prefix) {
        var list = [];
        var keys = Object.keys(headers);
    
        var fcHeaders = {};
        for (var i = 0; i < keys.length; i++) {
            var key = keys[i];
    
            var lowerKey = key.toLowerCase().trim();
            if (lowerKey.startsWith(prefix)) {
                list.push(lowerKey);
                fcHeaders[lowerKey] = headers[key];
            }
        }
        list.sort();
    
        var canonical = '';
        for (var _i = 0; _i < list.length; _i++) {
            var _key = list[_i];
            canonical += `${_key}:${fcHeaders[_key]}\n`;
        }
    
        return canonical;
    }            
    Authorization可由上文计算得出的signature构造出来,构造方法如下:
    Authorization = "FC " + accessKeyID + ":" + signature